#ifndef __GLF_STREAM_INCLUDED__
#define __GLF_STREAM_INCLUDED__

#include <glf/config.h>
#include <glf/core/inlines.h>

namespace glf {

namespace fs {
	enum Error {
		ERROR_NONE,
		ERROR_CORRUPT,
		ERROR_NOEXISTS,
		ERROR_ALREADYEXITS,
		ERROR_CANNOTCREATE,
		ERROR_MAXFILEHANDLE,
		ERROR_NOTEMPTY,
		ERROR_ACCESS,
		ERROR_NOTENOUGHMEMORY,
		ERROR_INVALIDFLAG,
		ERROR_NOTOPENED,
		ERROR_ISBUSY,
		ERROR_COUNT,
		ERROR_EOFFILE,
		ERROR_PATHTOOLONG
	};
}

struct ios {

enum seek {
	beg,
	cur,
	end
};

enum openflag {
#if GLF_PLATFORM_WP8 == 0
	// generic is a reserved keyword on windows phone 8.
	// the generic flag is deprecated, and should now be
	// replaced by: unrestricted
	//generic	= 0xffff, 
#endif

	unrestricted = 0xffff,
	none	= 0x0000,

	read	= 0x0001,
	write	= 0x0002,
	atend	= 0x0004,
	trunc	= 0x0008,
	create	= 0x0010,

	xread			= 0x0020,	//??
	xwrite			= 0x0040,	//??
	use_archive		= 0x0080,	//use this if you want to look for this file in archives.  

	in		= read,
	out		= write,
	io		= in|out,
	rw		= read|write,

	list_dir	= 0x0080,
	list_file	= 0x0100,
	list_hidden	= 0x0200,
	list_all	= list_hidden|list_dir|list_file,

	loc_shift	= 10,
	
	loc_data	= (0 << loc_shift),
	loc_home	= (1 << loc_shift),
	loc_tmp		= (2 << loc_shift),
	loc_raw		= (3 << loc_shift),
	loc_bak		= (4 << loc_shift),
	loc_app		= (5 << loc_shift),
	loc_cwd		= (6 << loc_shift),
	
	loc_mask	= (7 << loc_shift),	

	//								on ios, ~/ expands to /var/mobile/Applications/<uid>/
	loc_ios_root	=loc_raw,		// ~/
	loc_ios_app		=loc_data,		// ~/<bundle>.app
	loc_ios_doc		=loc_home,		// ~/Documents -> this folder is backuped with icloud
	loc_ios_tmp		=loc_tmp,		// ~/tmp
	loc_ios_lib		=loc_bak,		// ~/Library

	loc_win32_exe			= loc_app,	//
	loc_win32_starting_cwd	= 0,
	loc_win32_cwd			= 0,

	obfuscated	= 0x40000,
	check_crc	= 0x80000,

	specific	= 0xfff00000, 

	//psp specific
	psp			= specific,
	psp_umd		= 0x00100000,
	psp_mmc		= 0x00200000,

	//ps3 specific
	ps3			= specific,
	ps3_edat	= 0x00100000


};

};
//
//struct Location { enum E {
//	loc_generic_start = -1,
//	
//	loc_none,
//	loc_cwd,				// using cwd is not recommended, especially for multithreaded applications
//	loc_count,
//	
//	loc_specific_start = 0xff - 1,
//
//	//note that ~ expands to /var/mobile/Applications/<uid>/
//	loc_ios_start = loc_specific_start,
//	loc_ios_root,		// ~/ 
//	loc_ios_app,		// ~/<bundle>.app
//	loc_ios_doc,		// ~/Documents -> this folder is backuped with icloud
//	loc_ios_tmp,		// ~/tmp
//	loc_ios_lib,		// ~/Library
//	//loc_ios_caches,	// ~/Library/Caches
//	loc_ios_count,
//
//	loc_osx_start = loc_specific_start,
//
//	loc_win32_start = loc_specific_start,
//	loc_win32_exe,				// where the exe is located
//	loc_win32_home,				//env(APPDATA)
//	loc_win32_starting_cwd,		//
//	loc_win32_count,
//
//	loc_rvl_start = loc_specific_start,
//	loc_rvl_dvd,
//	loc_rvl_nand,
//	loc_rvl_cnt000,		//reserved
//	loc_rvl_cnt001,		//reserved
//	loc_rvl_cnt002,		//first useable cnt index
//	loc_rvl_cnt511 = loc_rvl_cnt000 + 512,
//	loc_rvl_count,
//
//}; };

//yes, this is a total rippoff of the nw4r::ut::IOStream/FileStream/DvdStream
class Stream : private NonCopyable, public ios {
public:
	typedef void (*Callback)(int result, Stream& stream, void* arg); //[dc] put const Stream&
	
	virtual ~Stream();
	
	fs::Error GetLastError() const;

	virtual void Close() = 0;

	virtual size_t Read(void* buffer, size_t size);
	virtual size_t ReadWithOffset(void* buffer, size_t size, size_t offset);
	virtual size_t Write(const void* buffer, size_t size);
	
	//virtual bool ReadASync(void* buffer, size_t size, Callback callback, void* arg);
	virtual bool ReadASync(void* buffer, size_t size, size_t offset, Callback callback, void* arg);
	//virtual bool WriteASync(const void* buffer, size_t size, Callback callback, void* arg);
	
	virtual size_t GetOffsetAlign() const;
	virtual size_t GetSizeAlign() const;
	virtual size_t GetBufferAlign() const;

	virtual bool CanWrite() const = 0;
	virtual bool CanRead() const = 0;
	//virtual bool CanAsync() const = 0;
	int WaitASync(size_t ms) const;

	virtual bool IsOpened() const;
	bool IsBusy() const;

	template<typename T>
	T ReadT() {
		T val;
		size_t read = Read(&val, sizeof(T));
		read; //this line removes the ununsed variable warning
		GLF_ASSERT(read == sizeof(T));
		return val;
	}

	template<typename T>
	bool ReadT(T& val) {
		size_t read = Read(&val, sizeof(T));
		return read == sizeof(T);
	}

	template<typename T, size_t N>
	size_t ReadTN(T (&val)[N]) {
		size_t read = Read(&val, N * sizeof(T));
		GLF_ASSERT((read % sizeof(T)) == 0);
		return read / sizeof(T); //== N*;
	}

	template<typename T>
	bool WriteT(const T& val) {
		size_t written = Write(&val, sizeof(T));
		return written == sizeof(T);
	}

	template<typename T, size_t N>
	size_t WriteTN(const T (&val)[N]) {
		size_t written = Write(&val, sizeof(T) * N);
		GLF_ASSERT((written % sizeof(T)) == 0);
		return written / sizeof(T);
	}
protected:
	Stream();

	fs::Error mLastError;
	bool mIsOpened;
	volatile bool mIsBusy;
	int mCallbackResult;
	Callback mCallback;
	void* mCallbackArg;
};


class IOStream : public Stream {
public:
	virtual size_t GetSize() const = 0;
	
	virtual bool CanSeek() const = 0;
	virtual bool Seek( int offset, seek origin );
	virtual size_t Tell()  const = 0;

	virtual size_t Peek(void* buffer, size_t size);
	
	template<typename T>
	T PeekT() {
		T val;
		size_t read = Peek(&val, sizeof(T));
		read;	//this line removes the ununsed variable warning
		GLF_ASSERT(read == sizeof(T));
		return val;
	}
	template<typename T, size_t N>
	bool PeekT(T (&val)[N]) {
		return Peek(&val, sizeof(T)*N) == sizeof(T)*N;
	}

	bool IsEof() const;
public:	//not sure 
	IOStream();
	struct FilePosition
	{
		FilePosition();
		void SetFileSize(size_t fileSize);
		size_t GetFileSize() const;
		
		size_t Skip( int offset );
		size_t Append( int offset );
		bool Seek( int offset, seek origin );
		size_t Tell() const;
	private:
		size_t mSize;
		size_t mPosition;
	} mFilePosition;
};

} //namespace glf

GLF_ENUM_FLAG(glf::ios::openflag)

#endif
